<?php
/**
 * Created by PhpStorm.
 * User: aiChenK
 * Date: 2018-12-26
 * Time: 9:28
 */
namespace Services\Sys;

use Helpers\Di;
use Helpers\Cache;
use Helpers\Tool;
use Services\Sys\Models\XhsysFunction;
use Services\Sys\Models\XhsysPower;
use Services\Sys\Models\XhsysTree;
use Services\Sys\Models\XhsysUserpower;

class Nav
{

    /**
     * 增加导航节点
     *
     * @param $params
     * @throws \Exception
     *
     * create by ck 20190107
     */
    public static function addNav($params)
    {
        try {
            Di::get()->db->begin();
            $tree = new XhsysTree();
            $tree->assign($params);
            if (!$tree->save()) {
                throw new \Exception($tree->getError());
            }
            if (Tool::getValue($params, 'sFunctionUrl')) {
                $fun = new XhsysFunction();
                $fun->assign($params);
                $fun->iNodeId = $tree->iNodeId;
                $fun->sFunctionName = $tree->sNodeName;
                if (!$fun->save()) {
                    throw new \Exception($fun->getError());
                }
            }
            Di::get()->db->commit();
            Cache::flush();
        } catch (\Exception $e) {
            Di::get()->db->rollback();
            throw new \Exception($e->getMessage());
        }
    }

    /**
     * 编辑导航节点
     *
     * @param $nodeId
     * @param $nodeName
     * @param $params
     * @throws \Exception
     *
     * create by ck 20190107
     */
    public static function editNav($nodeId, $nodeName, $params)
    {
        try {
            Di::get()->db->begin();
            $node = XhsysTree::findFirst($nodeId);
            if (!$node) {
                throw new \Exception('节点不存在');
            }
            $node->assign($params);
            if (!$node->save()) {
                throw new \Exception($node->getError());
            }
            $fun = XhsysFunction::findFirst([
                'conditions' => 'iNodeId = :nodeId:',
                'bind' => ['nodeId' => $nodeId]
            ]);
            if (!$fun) {
                $fun = new XhsysFunction();
            }
            $fun->assign($params);
            $fun->iNodeId = $nodeId;
            $fun->sFunctionName = $nodeName;
            if (!$fun->save()) {
                throw new \Exception($fun->getError());
            }
            Di::get()->db->commit();
            Cache::flush();
        } catch (\Exception $e) {
            Di::get()->db->rollback();
            throw new \Exception($e->getMessage());
        }
    }

    /**
     * 根据节点id获取导航节点信息
     *
     * @param $nodeId
     * @return array
     *
     * create by ck 20190107
     */
    public static function getNavInfo($nodeId)
    {
        $node     = XhsysTree::findFirst($nodeId);
        $parent   = XhsysTree::findFirst($node->iParentNodeId);
        $function = XhsysFunction::findFirst("iNodeId = {$nodeId} AND bDelete = 0");
        return [
            'iNodeId'           => $node->iNodeId,
            'sNodeName'         => $node->sNodeName,
            'sNodeShortName'    => $node->sNodeShortName,
            'sNodeIcon'         => $node->sNodeIcon,
            'iNodeType'         => $node->iNodeType,
            'iParentNodeId'     => $node->iParentNodeId,
            'sParentNodeName'   => $parent ? $parent->sNodeName : '',
            'sFunctionUrl'      => $function ? $function->sFunctionUrl : '',
            'sFunctionAttr'     => $function ? $function->sFunctionAttr : '',
        ];
    }

    /**
     * 删除导航节点
     *
     * @param $nodeId
     * @throws \Exception
     *
     * create by ck 20190107
     */
    public static function delNavNode($nodeId)
    {
        $node = XhsysTree::findFirst($nodeId);
        if (!$node) {
            throw new \Exception('节点不存在');
        }
        if (!$node->delete()) {
            throw new \Exception($node->getError());
        }
    }

    /**
     * 根据树节点遍历是否有子节点，有则连接到节点数组中，并循环执行函数
     * 如果没有子节点，则继续遍历，完成之后返回节点数组
     * 如果需要权限节点，查找nodeId下是否配置权限，无则删除自身节点
     * 如果有权限节点>1（有附加权限），且无子节点，添加一个子节点（可显示为文件夹）
     *
     * @param array $navNodes           --树节点数组
     * @param bool|false $powerNode     --是否权限节点
     * @param bool|true $useCache       --是否使用缓存
     * @return array                    --树节点数组
     *
     * create by ck 20160813
     * modify by ck 20160824
     * modify by ck 20160829    增加memcache缓存
     * modify by ck 20160902    增加sExtraUrl参数，用于扩展权限配置
     * modify by ck 20161020    增加useCache选项
     */
    public static function createNavNode($navNodes = [], $powerNode = false, $useCache = true)
    {
        if (!$powerNode && Cache::exists('navNodes_normal') && $useCache) {
            return Cache::get('navNodes_normal');
        }
        if ($powerNode && Cache::exists('navNodes_power') && $useCache) {
            return Cache::get('navNodes_power');
        }
        if (!$navNodes) {
            $navNodes = Di::get()->modelsManager->createBuilder()
                ->from(['tree' => '\\Services\\Sys\\Models\\XhsysTree'])
                ->columns(['tree.iNodeId', 'tree.sNodeName', 'tree.sNodeShortName', 'tree.iNodeType',
                    'tree.sNodeIcon', 'tree.iParentNodeId', 'fun.sFunctionUrl', 'fun.sFunctionAttr'])
                ->leftJoin(
                    '\\Services\\Sys\\Models\\XhsysFunction',
                    'fun.iNodeId = tree.iNodeId AND fun.bDelete = 0',
                    'fun'
                )
                ->where('tree.bDelete=0 AND tree.iNodeType<>2 AND tree.iParentNodeId=0')
                ->orderBy('tree.iNodeId')
                ->getQuery()
                ->execute()
                ->toArray();
            if (!$navNodes) {
                return [];
            }
            $navNodes = self::createNavNode($navNodes, $powerNode, $useCache);
            Cache::save($powerNode ? 'navNodes_power' : 'navNodes_normal', $navNodes);
            return $navNodes;
        }
        foreach ($navNodes as $key => $navNode) {
            $nodes = Di::get()->modelsManager->createBuilder()
                ->from(['tree' => '\\Services\\Sys\\Models\\XhsysTree'])
                ->columns(['tree.iNodeId', 'tree.sNodeName', 'tree.sNodeShortName', 'tree.iNodeType',
                    'tree.sNodeIcon', 'tree.iParentNodeId', 'fun.sFunctionUrl', 'fun.sFunctionAttr'])
                ->leftJoin(
                    '\\Services\\Sys\\Models\\XhsysFunction',
                    'fun.iNodeId = tree.iNodeId AND fun.bDelete=0',
                    'fun'
                )
                ->where('tree.bDelete=0 AND tree.iNodeType<>2 AND tree.iParentNodeId=' . $navNode['iNodeId'])
                ->getQuery()
                ->execute()
                ->toArray();

            if ($nodes) {
                $navNodes[$key]['oChildNode'] = self::createNavNode($nodes, $powerNode, $useCache);
            }
            if ($powerNode) {
                $powers = XhsysPower::find([
                    'conditions' => 'iNodeId = :nodeId:',
                    'columns'    => 'sPowerTag,sPowerName,bPowerMain,sExtraUrl',
                    'bind'       => ['nodeId' => $navNode['iNodeId']]
                ])->toArray();
                if (!$powers) {
                    unset($navNodes[$key]);
                    continue;
                }
                foreach ($powers as $power) {
                    if ($power['bPowerMain']) {
                        $navNodes[$key]['sPowerTag'] = $power['sPowerTag'];
                        $navNodes[$key]['sExtraUrl'] = $power['sExtraUrl'];
                    } else {
                        $navNodes[$key]['oPowerNode'][] = $power;
                    }
                }
            }
        }
        return $navNodes;
    }

    /**
     * 根据节点数组组合成导航树并返回
     *
     * @param array $navNodes
     * @param string $icon
     * @param string $treeStr
     * @param bool|true $useCache
     * @return string
     *
     * create by ck 20160814
     * modify by ck 20160822    添加参数$power，如果为true则icon失效，添加权限图标
     * modify by ck 20160829    删除$power参数，增加memcache缓存
     * modify by ck 20161020    添加$useCache参数，强制刷新（修改新增时使用）
     */
    public static function createNavTree($navNodes = [], $icon = null, $treeStr = null, $useCache = true)
    {
        $cacheKey = 'navTree' . ($icon ? '_' . $icon : '');
        if (Cache::exists($cacheKey) && $useCache) {
            return Cache::get($cacheKey);
        }
        if (!$navNodes) {
            $navNodes = self::createNavNode([], null, $useCache);
        }
        $iconStr = $icon ? '<i class="fa ' . $icon . '"></i>' : '';
        $liStr   = '';
        if (!$treeStr) {
            //如果有图标则添加class
            $treeStr .= '<div class="tree-view-menu-list font-16'
                . ($icon ? ' XH-Tree-View-Icon' : '')
                . '"><ul class="font-16">';
            $treeStr .= self::createNavTree($navNodes, $icon, $treeStr, $useCache);
            $treeStr .= '</ul></div>';
            Cache::save($cacheKey, $treeStr);
            return $treeStr;
        }
        foreach ($navNodes as $navNode) {
            $liCla  = isset($navNode['oChildNode'])
                ? ' class="openable' . ($navNode == end($navNodes) ? ' last-link' : '') . '"'
                : ($navNode == end($navNodes) ? ' class="last-link"' : '');
            $inIcon = isset($navNode['oChildNode'])
                ? '<i class="fa fa-folder m-right-xs folder-icon"></i>'
                : '<i class="fa fa-file m-right-xs"></i>';
            $liStr  .= '<li' . $liCla . '>';
            $liStr  .= '<a nodeId="' . $navNode['iNodeId'] . '">';
            //添加节点图片
            $liStr .= $inIcon;
            $liStr .= $navNode['sNodeName'];
            $liStr .= '</a>';
            //添加节点后图标（删除等按钮）
            $liStr .= $iconStr;
            $liStr .= isset($navNode['oChildNode'])
                ? ('<ul class="subtree">'
                    . self::createNavTree($navNode['oChildNode'], $icon, $liStr, $useCache)
                    . '</ul>')
                : '';
            $liStr .= '</li>';
        }
        return $liStr;
    }


    /**
     * 遍历树节点数组，未传入树节点数组则自动读取，如果类型不为1则跳过
     * 根据相应参数组成li并加入到导航字符串后返回，如果有子节点则嵌套执行，最多到3层
     *
     * @param array $navNodes   --树节点数组
     * @param string $navStr    --导航字符串（html代码）
     * @param int $nodeLevel    --节点等级，初始为1
     * @return string
     *
     * create by ck 20160814
     * modify by ck 20160829    增加memcache缓存
     * modify by ck 20160902    删除memcache缓存（不同人权限不同）
     * modify by ck 20160914    根据节点判断用户是否有权限，没有权限跳过
     * modify by ck 20161201    优化节点代码
     */
    public static function createNav($navNodes = array(), $navStr = '', $nodeLevel = 1)
    {
        if (!$navNodes) {
            $navNodes = self::createNavNode();
        }
        $liStr = '';
        $i     = 1;
        foreach ($navNodes as $navNode) {
            if ($navNode['iNodeType'] != 1) {
                continue;
            }
            //检查是否有权限
            if (!XhsysUserpower::checkUserPowerByNode($navNode['iNodeId'])) {
                continue;
            }
            //有子节点则添加'openable'类名
            $openAble = isset($navNode['oChildNode']) && $navNode['iNodeType'] <= 2 ? 'openable' : '';
            //检查是否子节点有type为1的节点
            if (isset($navNode['oChildNode'])) {
                foreach ($navNode['oChildNode'] as $childNode) {
                    if ($childNode['iNodeType'] == 1) {
                        $checkType = true;
                    }
                }
            }
            //没有子节点类型为1的节点则去掉'openable'属性
            if (!isset($checkType)) {
                $openAble = '';
            }
            //导航url中已有参数则将连接符改为&，用于添加nodeId
            $urlCont = strstr($navNode['sFunctionUrl'], '?') ? '&' : '?';
            //有导航url则添加到<a>标签的属性中，否则为''
            $btnAttr = $navNode['sFunctionUrl']
                ? " href='{$navNode['sFunctionUrl']}{$urlCont}nodeId={$navNode['iNodeId']}'
                target='{$navNode['sFunctionAttr']}'"
                : '';
            //第二级时给ul添加属性'third-level'，用于展示第三级
            $levelCla = $nodeLevel == 2 ? ' third-level' : '';
            //add by ck 20161201 start
            $ulStr = '';
            if (isset($navNode['oChildNode']) && $nodeLevel < 3) {
                $temp = self::createNav($navNode['oChildNode'], $navStr, $nodeLevel + 1);
                $temp and $ulStr = '<ul class="submenu' . $levelCla . '">' . $temp . '</ul>';
            }
            //第一级属性较多，分隔开
            if ($nodeLevel == 1) {
                $nodeIcon = $navNode['sNodeIcon'] ? $navNode['sNodeIcon'] : 'fa-folder';
                $menuSpan = isset($navNode['oChildNode']) ? '<span class="submenu-icon"></span>' : '';
                $liStr    .=
//                    '<li class="bg-palette2 '. $openAble .'">
                    '<li class="bg-palette' . ($i % 4 + 1) . ' ' . $openAble . '">
                        <a' . $btnAttr . '>
                            <span class="menu-content block">
                            <span class="menu-icon"><i class="fa ' . $nodeIcon . ' fa-lg"></i></span>
                            <span class="text m-left-sm">' . $navNode['sNodeName'] . '</span>
                            ' . $menuSpan . '
                            </span>
                            <span class="menu-content-hover block">' . $navNode['sNodeShortName'] . '</span>
                        </a>
                        ' . $ulStr . '
                    </li>';
            } else {
                $liStr .=
                    '<li class="' . $openAble . '">
                        <a' . $btnAttr . '>
                            <span class="submenu-label">' . $navNode['sNodeName'] . '</span>
                        </a>
                        ' . $ulStr . '
                    </li>';
            }
            $i++;
        }
        //返回原代码+生成的li代码
        return $navStr . $liStr;
    }

    /**
     * 根据节点id生成面包屑
     *
     * @param $nodeId                   --节点ID
     * @param bool|false $childNames    --子节点（array|string）
     * @param bool|false $showBack      --显示返回按钮
     * @return string
     *
     * create by ck 20160813
     * modify by ck 20160829    增加memcache缓存
     * modify by ck 20170706    添加子节点参数
     * modify by ck 20170904    展示返回按钮
     */
    public static function createNavUl($nodeId, $childNames = null, $showBack = false)
    {
        if (!$childNames && Cache::exists('navUl_' . $nodeId)) {
            return Cache::get('navUl_' . $nodeId);
        }
        $childLiStr = '';
        if ($childNames) {
            is_string($childNames) and $childNames = [$childNames];
            foreach ($childNames as $name) {
                $childLiStr .= "<li>{$name}</li>";
            }
        }
        $navUlStr = '<ul class="breadcrumb">'
            . '<li><i class="fa fa-home fa-lg"></i> 主页</li>'
            . self::createChildNavLi($nodeId)
            . $childLiStr
            . ($showBack ? '<a onclick="history.go(-1)" class="btn btn-success btn-sm xh-nav-back">返回</a>' : '')
            . '</ul>';
        $childNames or Cache::save('navUl_' . $nodeId, $navUlStr);
        return $navUlStr;
    }

    /**
     * 根据节点id生成子节点面包屑，如有父节点则继续调用自身
     *
     * @param $nodeId
     * @return string
     *
     * create by ck 20170706
     */
    private static function createChildNavLi($nodeId)
    {
        $node  = XhsysTree::findFirst($nodeId);
        $navLi = '<li>' . $node->sNodeName . '</li>';
        if ($node->iParentNodeId != 0) {
            $navLi = self::createChildNavLi($node->iParentNodeId) . $navLi;
        }
        return $navLi;
    }

}
